/*
 *  lock_CV_test.c
 *  
 *
 *  Created by Wang Zhuchen on 2/13/12.
 *  Copyright 2012 __MyCompanyName__. All rights reserved.
 *
 */

#include "syscall.h"
#include "userlib.h"

/* Mallicious test
 * In this test we are going to acquire and 
 * release lock illegally
 */
void lockTest() {
	int arg[2];
	int testLock = CreateLock("testLock", sizeof("testLock"));
	arg[0] = testLock;
	myprintf("Lock %d created\n", arg, 1);
	arg[0] = -2;
	myprintf("Try to acquire an illegal lock %d\n", arg, 1);
	AcquireLock(-2);
	arg[0] = testLock+1;
	myprintf("Try to acquire an illegal lock %d\n", arg, 1);
	AcquireLock(testLock+1);
	arg[0] = testLock;
	myprintf("Try to acquire a legal lock %d\n", arg, 1);
	AcquireLock(testLock);
	
	arg[0] = -2;
	myprintf("Try to release a legal lock %d\n", arg, 1);
	ReleaseLock(-2);
	arg[0] = testLock+1;
	myprintf("Try to release an illegal lock %d\n", arg, 1);
	ReleaseLock(testLock+1);
	arg[0] = testLock;
	myprintf("Try to release a legal lock %d\n", arg, 1);
	ReleaseLock(testLock);
	DestroyLock(testLock);
}

void wait(int time) {
	int i;
	for (i=0; i<time; i++) {
		Yield();
	}
}

/* Test 1 */
int t1_l1;	/* the lock tested in Test 1 */

/* test 1 thread 1
 * This is the rightful lock owner
 */
void t1_t1() {
	int i;
	int arg[2];
	AcquireLock(t1_l1);
	arg[0] = 1;
	arg[1] = t1_l1;
	myprintf("t1_t%d: Acquired Lock %d, waiting for t3\n", arg, 2);
	myprintf("t1_t%d: working in CS\n", arg, 2);
	wait(10);
	myprintf("t1_t%d: Releasing lock %d\n", arg, 2);
	ReleaseLock(t1_l1);
	Exit(0);
}

/* test 1 thread 2
 * This thread will wait on the held lock
 */
void t1_t2() {
	int i;
	int arg[2];
	arg[0] = 2;
	arg[1] = t1_l1;
	wait(2);
	myprintf("t1_t%d: trying to acquire lock %d\n", arg, 2);
	AcquireLock(t1_l1);
	myprintf("t1_t%d: Acquired lock %d\n", arg, 2);
	wait(10);
	myprintf("t1_t%d: Releasing lock %d\n", arg, 2);
	ReleaseLock(t1_l1);
	Exit(0);
}

/* test 1 thread 3
 * This thread will try to release the lock illegally
 */
void t1_t3() {
	int i;
	int arg[2];
	arg[0] = 3;
	arg[1] = t1_l1;
	wait(3);
	for (i=0; i<3; i++) {
		myprintf("t1_t%d: trying to release lock %d\n", arg, 2);
		ReleaseLock(t1_l1);
	}
	Exit(0);
}

/* Test 2 */

int t2_l1;	/* For mutual exclusion */
int t2_c1;	/* The condition variable to test */

/* test 2 thread 1
 * This thread will signal a variable with nothing waiting
 */
void t2_t1() {
	int arg[3];
	arg[0] = 1;
	arg[1] = t2_l1;
	arg[2] = t2_c1;
	AcquireLock(t2_l1);
	myprintf("t2_t%d: Lock %d acquired, signalling %d\n", arg, 3);
	SignalCondition(t2_c1, t2_l1);
	myprintf("t2_t%d: Releasing Lock %d\n", arg, 3);
	ReleaseLock(t2_l1);
	Exit(0);
}

/* test 2 thread 2
 * This thread will wait on a pre-signalled variable
 */
void t2_t2() {
	int arg[3];
	arg[0] = 2;
	arg[1] = t2_l1;
	arg[2] = t2_c1;
	wait(5);
	AcquireLock(t2_l1);
	myprintf("t2_t%d: Lock %d acquired, waiting on %d\n", arg, 3);
	WaitCondition(t2_c1, t2_l1);
	myprintf("t2_t%d: Releasing Lock %d\n", arg, 3);
	ReleaseLock(t2_l1);
	Exit(0);
}

/* Test 3 */
int t3_l1;	/* For mutual exclusion */
int t3_c1;	/* The condition variable to test */
int t3_l2;
int num = 0;

/* t3_waiter()
 * These threads will wait on the t3_c1 condition variable.
 * Only one t3_waiter will be released
 */
void t3_waiter() {
	int arg[3];
	AcquireLock(t3_l1);
	arg[0] = num ++;
	ReleaseLock(t3_l1);
	arg[1] = t3_l2;
	arg[2] = t3_c1;
	
	AcquireLock(t3_l2);
	myprintf("t3_waiter%d: Lock %d acquired, waiting on %d\n", arg, 3);
	WaitCondition(t3_c1, t3_l2);
	arg[1] = t3_c1;
	myprintf("t3_waiter%d: free from %d\n", arg, 2);
	ReleaseLock(t3_l2);
	Exit(0);
}

/* t3_signaller()
 * This thread will signal the t3_c1 condition variable.
 * Only one t3_signaller will be released
 */
void t3_signaller() {
	int arg[3];
	arg[0] = 0;
	arg[1] = t3_l2;
	arg[2] = t3_c1;
	wait(5);
	AcquireLock(t3_l2);
	myprintf("t3_signaller%d: Lock %d acquired, signalling %d\n", arg, 3);
	SignalCondition(t3_c1, t3_l2);
	ReleaseLock(t3_l2);
	Exit(0);
}

/* Test 4 */

int t4_l1;
int t4_l2;
int t4_c1;
int num4 = 0;

/* t4_waiter()
 * These threads will wait on the t4_c1 condition variable.
 * All t4_waiters will be released
 */
void t4_waiter() {
	int arg[3];
	AcquireLock(t4_l1);
	arg[0] = num4 ++;
	ReleaseLock(t4_l1);
	arg[1] = t4_l2;
	arg[2] = t4_c1;
	
	AcquireLock(t4_l2);
	myprintf("t4_waiter%d: Lock %d acquired, waiting on %d\n", arg, 3);
	WaitCondition(t4_c1, t4_l2);
	arg[1] = t4_c1;
	myprintf("t4_waiter%d: free from %d\n", arg, 2);
	ReleaseLock(t4_l2);
	Exit(0);
}

/* t4_signaller()
 * This thread will broadcast the t4_c1 condition variable.
 * All t4_waiter will be released
 */
void t4_signaller() {
	int arg[3];
	arg[0] = 0;
	arg[1] = t4_l2;
	arg[2] = t4_c1;
	wait(5);
	AcquireLock(t4_l2);
	myprintf("t4_signaller%d: Lock %d acquired, broadcasting %d\n", arg, 3);
	BroadcastCondition(t4_c1, t4_l2);
	ReleaseLock(t4_l2);
	Exit(0);
}

/* Test 5 */

int t5_l1;	/* For mutual exclusion */
int t5_l2;	/* Second lock for the bad behavior */
int t5_c1;	/* The condition variable to test */

/* t5_t1() -- test 5 thread 1
 * This thread will wait on a condition under t5_l1
 */
void t5_t1() {
	int arg[3];
	arg[0] = 1;
	arg[1] = t5_l1;
	arg[2] = t5_c1;
	AcquireLock(t5_l1);
	myprintf("t5_t%d: Lock %d acquired, waiting on %d\n", arg, 3);
	WaitCondition(t5_c1, t5_l1);
	ReleaseLock(t5_l1);
	Exit(0);
}

/* t5_t2() -- test 5 thread 2
 * This thread will wait on a t5_c1 condition under t5_l2, which is
 * a Fatal error
 */
void t5_t2() {
	int arg[3];
	arg[0] = 2;
	arg[1] = t5_l2;
	arg[2] = t5_c1;
	wait(5); /* Wait for t5_t1 to get into the monitor */
	AcquireLock(t5_l1);
	AcquireLock(t5_l2);
	myprintf("t5_t%d: Lock %d acquired, signalling on %d\n", arg, 3);
	SignalCondition(t5_c1, t5_l2);
	myprintf("t5_t%d: Releasing Lock %d\n", arg, 2);
	ReleaseLock(t5_l2);
	arg[1] = t5_l1;
	myprintf("t5_t%d: Releasing Lock %d\n", arg, 2);
	ReleaseLock(t5_l1);
	Exit(0);
}

/* Test 6 */

int t6_l1;	/* Test destroy lock */

/* t6_t1() -- test 6 thread 1
 * This thread will acquire a lock and destory it before release;
 */
void t6_t1() {
	int arg[2];
	arg[0] = 1;
	arg[1] = t6_l1;
	AcquireLock(t6_l1);
	myprintf("t6_t%d: Lock %d acquired\n", arg, 2);
	wait(5);
	myprintf("t6_t%d: Delete the lock %d\n", arg, 2);
	DestroyLock(t6_l1);
	myprintf("t6_t%d: Release the lock %d\n", arg, 2);
	ReleaseLock(t6_l1);
	Exit(0);
}

/* t6_t2() -- test 6 thread 2
 * This thread will still get the lock after t6_t1 destroy the lock
 */
void t6_t2() {
	int arg[2];
	arg[0] = 2;
	arg[1] = t6_l1;
	wait(3);
	myprintf("t6_t%d: Try to acquire lock %d\n", arg, 2);
	AcquireLock(t6_l1);
	myprintf("t6_t%d: Lock %d acquired\n", arg, 2);
	wait(2);
	myprintf("t6_t%d: Release the lock %d\n", arg, 2);
	ReleaseLock(t6_l1);
	Exit(0);
}

/* Test 7 */
int t7_l1;	/* the lock used by CV */
int t7_c1;	/* the condition variable will be deleted before signal */

/* t7_t1() -- test 7 thread 1
 * This thread will wait on a deleted condition variable
 */
void t7_t1() {
	int arg[3];
	arg[0] = 1;
	arg[1] = t7_l1;
	arg[2] = t7_c1;
	AcquireLock(t7_l1);
	myprintf("t7_t%d: Lock %d acquired, wait on Condition %d\n", arg, 3);
	WaitCondition(t7_c1, t7_l1);
	arg[1] = t7_c1;
	arg[2] = t7_l1;
	myprintf("t7_t%d: Free from %d, Rlease Lock %d\n", arg, 3);
	ReleaseLock(t7_l1);
	Exit(0);
}

/* t7_t2() -- test 7 thread 2
 * This thread will delete the condition variable 
 */
void t7_t2() {
	int arg[2];
	arg[0] = 2;
	arg[1] = t7_c1;
	wait(5);
	myprintf("t7_t%d: Delete the Condition %d\n", arg, 2);
	DestroyCondition(t7_c1);
	Exit(0);
}

/* t7_t3() -- test 7 thread 3
 * This thread will signal the waiter
 */
void t7_t3() {
	int arg[3];
	arg[0] = 3;
	arg[1] = t7_l1;
	arg[2] = t7_c1;
	wait(10);
	AcquireLock(t7_l1);
	myprintf("t7_t%d: Lock %d acquired, signal Condition %d\n", arg, 3);
	SignalCondition(t7_c1, t7_l1);
	myprintf("t7_t%d: Rlease Lock %d\n", arg, 2);
	ReleaseLock(t7_l1);
	Exit(0);
}

int main() {
	int i;
	
	/* Single Thread Test */
	lockTest();
	
	/* Test 1 */
	myprintf("Starting Test 1\n", 0, 0);
	t1_l1 = CreateLock("t1_l1", 5);
	Fork(t1_t1);
	Fork(t1_t3);
	Fork(t1_t2);
	wait(25);
	
	/* Test 2 */
	myprintf("Starting Test 2.  Note that it is an error if thread t2_t2 completes\n", 0, 0);
	t2_l1 = CreateLock("t2_l1", 5);
	t2_c1 = CreateCondition("t2_c1", 5);
	Fork(t2_t1);
	Fork(t2_t2);
	wait(10);
	
	/* Test 3 */
	myprintf("Starting Test 3\n", 0, 0);
	t3_l1 = CreateLock("t3_l1", 5);
	t3_l2 = CreateLock("t3_l2", 5);
	t3_c1 = CreateCondition("t3_c1", 5);
	for (i=0; i<5; i++) {
		Fork(t3_waiter);
	}
	Fork(t3_signaller);
	wait(15);
	
	/* Test 4 */
	myprintf("Starting Test 4\n", 0, 0);
	t4_l1 = CreateLock("t4_l1", 5);
	t4_l2 = CreateLock("t4_l2", 5);
	t4_c1 = CreateCondition("t4_c1", 5);
	for (i=0; i<5; i++) {
		Fork(t4_waiter);
	}
	Fork(t4_signaller);
	wait(15);
	
	/* Test 5 */
	myprintf("Starting Test 5.  Note that it is an error if thread t5_t1 completes\n", 0, 0);
	t5_l1 = CreateLock("t5_t1", 5);
	t5_l2 = CreateLock("t5_l2", 5);
	t5_c1 = CreateCondition("t5_c1", 5);
	Fork(t5_t1);
	Fork(t5_t2);
	wait(10);
	
	/* Test 6 */
	myprintf("Starting Test 6. Both threads can exit normally\n", 0, 0);
	t6_l1 = CreateLock("t6_l1", 5);
	Fork(t6_t1);
	Fork(t6_t2);
	wait(10);
	
	DestroyLock(t1_l1);
	DestroyLock(t2_l1);
	DestroyLock(t3_l1);
	DestroyLock(t3_l2);
	DestroyLock(t4_l1);
	DestroyLock(t4_l2);
	DestroyLock(t5_l1);
	DestroyLock(t5_l2);
	DestroyCondition(t2_c1);
	DestroyCondition(t3_c1);
	DestroyCondition(t4_c1);
	DestroyCondition(t5_c1);
	
	
	/* Test 7 */
	myprintf("Starting Test 7. All threads can exit normally\n", 0, 0);
	t7_l1 = CreateLock("t7_l1", 5);
	t7_c1 = CreateCondition("t7_c1", 5);
	Fork(t7_t1);
	Fork(t7_t2);
	Fork(t7_t3);
	wait(20);
	DestroyLock(t7_l1);
	return 0;
}